package cn.s1995.sloth.admin.api.utils;

import cn.hutool.json.JSONUtil;
import lombok.Data;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.javacv.*;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

/**
 * VideoUtil
 *
 * @author Lex
 * @description
 * @date 2024/01/30 15:28
 */
@UtilityClass
@Slf4j
public class VideoUtil {

    public static void main(String[] args) throws FFmpegFrameRecorder.Exception, FrameGrabber.Exception {
        String videoUrl = "C:\\Users\\user\\Videos\\592e0b3fb57fcdc1374b0ed1f54fed9a.mp4";
        String outputImage = "C:\\Users\\user\\Videos\\592e0b3fb57fcdc1374b0ed1f54fed9a.png";
        // 打印耗时时间
        long start = System.currentTimeMillis();


        // VideoInfo videoInfo = getVideoInfo(new File(videoUrl));
        // System.out.println(videoInfo.toJson());

        // captureFrameAtSecond(videoUrl, outputImage, 255);

        compressVideo(videoUrl, "C:\\Users\\user\\Videos\\592e0b3fb57fcdc1374b0ed1f54fed9a_compress.mp4");

        long end = System.currentTimeMillis();
        System.out.println("耗时：" + (end - start) + "ms");

    }

    /**
     * 视频信息
     *
     * @author Lex
     * @description
     * @date 2024/1/30 15:28
     */
    @Data
    public static class VideoInfo {
        /**
         * 总帧数
         **/
        private int lengthInFrames;

        /**
         * 帧率
         **/
        private double frameRate;

        /**
         * 时长
         **/
        private double duration;

        /**
         * 视频编码
         */
        private String videoCode;
        /**
         * 音频编码
         */
        private String audioCode;

        private int width;
        private int height;
        private int audioChannel;
        private String md5;
        /**
         * 音频采样率
         */
        private Integer sampleRate;

        public String toJson() {
            return JSONUtil.toJsonStr(this);
        }
    }


    /**
     * 获取视频信息
     * @param videoFilePath
     * @return
     */
    public static VideoInfo getVideoInfo(String videoFilePath) {
        VideoInfo videoInfo = new VideoInfo();
        FFmpegFrameGrabber grabber = null;
        try {
            grabber = new FFmpegFrameGrabber(videoFilePath);
            // 启动 FFmpeg
            grabber.start();

            // 读取视频帧数
            videoInfo.setLengthInFrames(grabber.getLengthInVideoFrames());

            // 读取视频帧率
            videoInfo.setFrameRate(grabber.getVideoFrameRate());

            // 读取视频秒数
            videoInfo.setDuration(grabber.getLengthInTime() / 1000000.00);

            // 读取视频宽度
            videoInfo.setWidth(grabber.getImageWidth());

            // 读取视频高度
            videoInfo.setHeight(grabber.getImageHeight());


            videoInfo.setAudioChannel(grabber.getAudioChannels());

            videoInfo.setVideoCode(grabber.getVideoCodecName());

            videoInfo.setAudioCode(grabber.getAudioCodecName());
            // String md5 = MD5Util.getMD5ByInputStream(new FileInputStream(videoFilePath));

            videoInfo.setSampleRate(grabber.getSampleRate());
            return videoInfo;
        } catch (Exception e) {
            log.error("getVideoInfo failed 获取文件信息失败：{}", e.getMessage());
            return null;
        } finally {
            try {
                if (grabber != null) {
                    // 此处代码非常重要，如果没有，可能造成 FFmpeg 无法关闭
                    grabber.stop();
                    grabber.release();
                }
            } catch (FFmpegFrameGrabber.Exception e) {
                log.error("getVideoInfo grabber.release failed 获取文件信息失败：{}", e.getMessage());
            }
        }
    }

    /**
     * 截取视频指定秒数的帧
     * @param videoFilePath 视频文件路径
     * @param outputPath    截取的帧的保存路径
     * @param second        指定秒数
     */
    public static void captureFrameAtSecond(String videoFilePath, String outputPath, int second) throws IOException {
        captureFrameAtSecond(new File(videoFilePath), new File(outputPath), second);
    }


    /**
     * 截取视频指定秒数的帧
     * @param videoFile 视频文件路径
     * @param outputFile    截取的帧的保存路径
     * @param second        指定秒数
     */
    public static void captureFrameAtSecond(File videoFile, File outputFile, int second) throws IOException {
        try (FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(videoFile)) {
            grabber.start();

            // 获取视频总时长（秒）
            double lengthInSeconds = grabber.getLengthInTime() / 1_000_000.0;
            int targetSecond = second;

            if (second >= lengthInSeconds) {
                // 如果指定秒数超过视频总时长，设置为最后一秒
                targetSecond = (int) lengthInSeconds - 1;
            }

            // 计算目标帧号
            int frameNumber = (int) (targetSecond * grabber.getFrameRate());
            grabber.setFrameNumber(frameNumber);

            Frame frame = grabber.grabImage();
            if (frame != null) {
                BufferedImage bufferedImage = new Java2DFrameConverter().convert(frame);
                ImageIO.write(bufferedImage, "png", outputFile);
            }
        } catch (IOException e) {
            log.error("captureFrameAtSecond failed: {}", e.getMessage(), e);
            throw e;
        }
    }

    /**
     * 压缩视频
     * @param inputPath
     * @param outputPath
     */
    public static void compressVideo(String inputPath, String outputPath) throws FFmpegFrameRecorder.Exception, FrameGrabber.Exception {
       compressVideo(new File(inputPath), new File(outputPath));
    }

    /**
     * 压缩视频
     * @param originFile
     * @param newFile
     */
    public static void compressVideo(File originFile, File newFile) throws FFmpegFrameRecorder.Exception, FrameGrabber.Exception {
        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(originFile);
        FFmpegFrameRecorder recorder = null;
        try {
            grabber.start();

            recorder = new FFmpegFrameRecorder(newFile, grabber.getImageWidth(), grabber.getImageHeight(), grabber.getAudioChannels());
            recorder.setFormat(grabber.getFormat());
            recorder.setSampleRate(grabber.getSampleRate());
            recorder.setFrameRate(grabber.getFrameRate());
            recorder.setVideoCodec(grabber.getVideoCodec()); // 设置视频编码
            recorder.setVideoBitrate(grabber.getVideoBitrate() / 2); // 减少比特率以压缩视频

            recorder.start();

            Frame frame;
            while ((frame = grabber.grabFrame()) != null) {
                recorder.record(frame);
            }
        } catch (Exception e) {
            log.error("compressVideo failed: {}", e.getMessage(), e);
            throw e;
        } finally {
            try {
                if (recorder != null) recorder.stop();
                grabber.stop();
            } catch (Exception e) {
                log.error("compressVideo failed: {}", e.getMessage(), e);
                throw e;
            }
        }
    }

}
